package cn.yunyichina.provider.pay.service.impl;

import cn.yunyichina.provider.framework.kafka.KafkaTopic;
import cn.yunyichina.provider.framework.utils.UUIDUtil;
import cn.yunyichina.provider.iface.entity.alipay.Alipay;
import cn.yunyichina.provider.iface.entity.base.OrderExtVo;
import cn.yunyichina.provider.iface.entity.pay.MchData;
import cn.yunyichina.provider.iface.entity.pay.PayCode;
import cn.yunyichina.provider.iface.entity.pay.notify.PacityPayNotifyMessage;
import cn.yunyichina.provider.iface.entity.pay.notify.PayNotifyWechatMedicalMessage;
import cn.yunyichina.provider.iface.entity.pay.notify.UnifiedPayNotifyMessage;
import cn.yunyichina.provider.pay.config.Pacity;
import cn.yunyichina.provider.pay.constant.Medicare;
import cn.yunyichina.provider.pay.dao.PaymentJournalDao;
import cn.yunyichina.provider.pay.entity.PaymentJournal;
import cn.yunyichina.provider.pay.entity.PaymentSetting;
import cn.yunyichina.provider.pay.entity.notify.AlipayAttach;
import cn.yunyichina.provider.pay.entity.notify.WechatPayAttach;
import cn.yunyichina.provider.pay.executor.PayNotifyExecutor;
import cn.yunyichina.provider.pay.executor.PayNotifyWorker;
import cn.yunyichina.provider.pay.service.BaseNotifyService;
import cn.yunyichina.provider.pay.service.NotifyService;
import cn.yunyichina.provider.pay.service.PaymentJournalService;
import cn.yunyichina.provider.pay.utils.NumberUtil;
import cn.yunyichina.utils.commons.DateUtils;
import cn.yunyichina.utils.convert.JsonUtils;
import cn.yunyichina.utils.convert.XmlUtils;
import cn.yunyichina.utils.security.SignUtil;
import com.alibaba.fastjson.JSON;
import com.alipay.api.internal.util.AlipaySignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.kafka.support.SendResult;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.ListenableFutureCallback;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;

/**
 * 支付通知
 * Created by Lullaby on 2016/10/19.
 */
@Service
public class NotifyServiceImpl extends BaseNotifyService implements NotifyService {

    private static final Logger logger = LoggerFactory.getLogger(NotifyServiceImpl.class);

    private static final String SUCCESS = "SUCCESS";

    private static final String FAIL = "FAIL";

    private static final String FAILURE = "FAILURE";

    @Resource
    private PayNotifyExecutor notifyExecutor;

    @Resource
    private KafkaTemplate<Integer, String> kafkaTemplate;

    @Resource
    private PaymentJournalDao paymentJournalDao;

    @Resource
    private PaymentJournalService paymentJournalService;

    public String testNotifyMessage(String data) {
        logger.info("NotifyServiceImpl.testNotifyMessage.data -> {}", data);

        try {
            Map map = JsonUtils.parseObject(data, Map.class);

            assert map != null;
            String topic = (String) map.get("topic");
            Object message = map.get("message");

            ListenableFuture<SendResult<Integer, String>> future = kafkaTemplate.send(topic, JSON.toJSONString(message));
            future.addCallback(new ListenableFutureCallback<SendResult<Integer, String>>() {
                public void onSuccess(SendResult<Integer, String> sendResult) {
                    logger.info("NotifyServiceImpl.testNotifyMessage.callback.onSuccess.getProducerRecord -> {}", sendResult.getProducerRecord().toString());
                    logger.info("NotifyServiceImpl.testNotifyMessage.callback.onSuccess.getRecordMetadata -> {}", sendResult.getRecordMetadata().toString());
                }

                public void onFailure(Throwable throwable) {
                    logger.error("NotifyServiceImpl.produceMessage.onFailure", throwable);
                }
            });
        } catch (Exception e) {
            logger.error("NotifyServiceImpl.testNotifyMessage.Exception", e);
            return "failure";
        }

        return "success";
    }

    /**
     * 微信支付通知
     *
     * @param data
     * @return
     */
    public String wechatNotify(String data) {
        logger.info("NotifyServiceImpl.wechatNotify.data -> {}", data);
        if (StringUtils.isEmpty(data)) return printWechatPayNotifyFailureMessage();

        try {
            Map<String, String> params = XmlUtils.xmlToMap(data);
            logger.info("NotifyServiceImpl.wechatNotify.params -> {}", JsonUtils.toJsonString(params));

            String returnCode = params.get("return_code");
            if (FAIL.equals(returnCode)) return printWechatPayNotifyFailureMessage();

            String resultCode = params.get("result_code");
            if (FAIL.equals(resultCode)) return printWechatPayNotifyFailureMessage();

            PaymentSetting paymentSetting = this.getPaymentSetting(params.get("appid"));
            boolean isSignValid = SignUtil.validateWechatPaySign(params, paymentSetting.getPayKey());
            logger.info("NotifyServiceImpl.wechatNotify.isSignValid -> {}", isSignValid);
            if (!isSignValid) return printWechatPayNotifyFailureMessage();

            String orderNo = params.get("out_trade_no");
            String paymentDealId = params.get("transaction_id");
            if (isPayNotified(orderNo, paymentDealId)) return printWechatPayNotifySuccessMessage();

            return doOrdinaryWechatPayNotify(params, data);
        } catch (Exception e) {
            logger.error("NotifyServiceImpl.wechatNotify.exception", e);
            return printWechatPayNotifyFailureMessage();
        }
    }

    /**
     * 微信医疗支付通知
     *
     * @param data
     * @return
     */
    public String medicalNotifyWechat(String data) {
        logger.info("NotifyServiceImpl.medicalNotifyWechat.data -> {}", data);
        if (StringUtils.isEmpty(data)) return printWechatPayNotifyFailureMessage();

        try {
            Map<String, String> params = XmlUtils.xmlToMap(data);
            logger.info("NotifyServiceImpl.medicalNotifyWechat.params -> {}", JsonUtils.toJsonString(params));

            String returnCode = params.get("return_code");
            if (FAIL.equals(returnCode)) return printWechatPayNotifyFailureMessage();

            PaymentSetting paymentSetting = this.getPaymentSetting(params.get("appid"));
            String key = paymentSetting.getMedicarePayKey();
            boolean isSignValid = SignUtil.validateWechatPaySign(params, key);
            logger.info("NotifyServiceImpl.medicalNotifyWechat.isSignValid -> {}", isSignValid);
            if (!isSignValid) return printWechatMedicalPayNotifyMessage(false, key);

            String resultCode = params.get("result_code");
            if (FAIL.equals(resultCode)) return printWechatMedicalPayNotifyMessage(false, key);

            String orderNo = params.get("hosp_out_trade_no");
            String paymentDealId = params.get("med_trans_id");
            if (isPayNotified(orderNo, paymentDealId)) return printWechatMedicalPayNotifyMessage(true, key);

            return doMedicalWechatPayNotify(params, key);
        } catch (Exception e) {
            logger.error("NotifyServiceImpl.medicalNotifyWechat.exception", e);
            return printWechatPayNotifyFailureMessage();
        }
    }

    /**
     * 微信医疗支付通知逻辑
     *
     * @param params
     * @return
     */
    private String doMedicalWechatPayNotify(final Map<String, String> params, String key) {
        OrderExtVo orderExtVo = this.getBaseOrderByOrderNo(params.get("hosp_out_trade_no"));

        // 1.产出消息统一结构体字符串
        try {
            produceMessage(KafkaTopic.WECHAT_MEDICAL_PAY.toString(), JsonUtils.toJsonString(buildWechatMedicalPayMessage(params)));
        } catch (Exception e) {
            logger.error("NotifyServiceImpl.doMedicalWechatPayNotify.produceMessage.Exception", e);
        }

        // 2.写入云医支付流水
        try {
            insertPaymentJournal(createWechatMedicalJournal(params, orderExtVo, getWechatMedicalPayAttach()));
        } catch (Exception e) {
            logger.error("NotifyServiceImpl.doMedicalWechatPayNotify.insertPaymentJournal.Exception", e);
        }

        return printWechatMedicalPayNotifyMessage(true, key);
    }

    private WechatPayAttach getWechatMedicalPayAttach() {
        WechatPayAttach attach = new WechatPayAttach();
        attach.setPayCode(PayCode.WXMEDICALPAY.toString());
        attach.setTopic(KafkaTopic.WECHAT_MEDICAL_PAY.toString());
        return attach;
    }

    /**
     * 构建微信医疗支付消息体
     *
     * @param params
     * @return
     */
    private PayNotifyWechatMedicalMessage buildWechatMedicalPayMessage(Map<String, String> params) {
        PayNotifyWechatMedicalMessage message = new PayNotifyWechatMedicalMessage();
        message.setAppid(params.get("appid"));
        message.setOrderNo(params.get("hosp_out_trade_no"));
        message.setPayOrderNo(params.get("med_trans_id"));
        message.setPayAmount(params.get("cash_fee"));
        message.setTotalAmount(params.get("total_fee"));
        message.setPayTime(DateUtils.parseDatetime(params.get("time_end"), DateUtils.YYYYMMDDHHMMSS_NOBAR));

        message.setMchId(params.get("mch_id"));
        message.setOpenid(params.get("openid"));

        message.setSubAppid(params.get("sub_appid"));
        message.setSubMchId(params.get("sub_mch_id"));
        message.setSubOpenid(params.get("sub_openid"));
        message.setInsuranceAmount(params.get("insurance_fee"));
        message.setMedicalCardId(params.get("medical_card_id"));
        message.setResponseContent(params.get("response_content"));

        message.setAttach(JsonUtils.toJsonString(getWechatMedicalPayAttach()));

        return message;
    }

    /**
     * 创建微信医疗支付对应云医支付流水对象
     *
     * @param params
     * @param attach
     * @return
     */
    private PaymentJournal createWechatMedicalJournal(Map<String, String> params, OrderExtVo orderExtVo, WechatPayAttach attach) {
        PaymentJournal journal = new PaymentJournal();

        journal.setId(UUIDUtil.get32UUID());
        journal.setPaymentDealNo(NumberUtil.buildPaymentDealNo());

        journal.setOrgId(orderExtVo.getOrgId());
        journal.setOrgCode(orderExtVo.getOrgCode());
        journal.setOrgName(orderExtVo.getOrgName());
        journal.setPlatformId(orderExtVo.getPlatformId());
        journal.setPlatformName(orderExtVo.getPlatformName());
        journal.setPlatformOrgAppCode(orderExtVo.getPlatformOrgAppCode());
        journal.setPlatformOrgAppName(orderExtVo.getPlatformOrgAppName());
        journal.setBranchId(orderExtVo.getBranchId());
        journal.setBranchCode(orderExtVo.getBranchCode());
        journal.setBranchName(orderExtVo.getBranchName());
        journal.setOutOrderNo(orderExtVo.getOutOrderNo());
        journal.setOrderType(orderExtVo.getOrderType());
        journal.setAccountAmount(orderExtVo.getAccountAmount());
        journal.setMedicareAmount(orderExtVo.getMedicareAmount());
        journal.setInsuranceAmount(Integer.valueOf(params.get("insurance_fee") == null ? "0" : params.get("insurance_fee")));
        journal.setPayAmount(Integer.valueOf(params.get("cash_fee") == null ? "0" : params.get("cash_fee")));
        journal.setTotalAmount(journal.getInsuranceAmount() + journal.getPayAmount());
        journal.setInsuranceMode(orderExtVo.getInsuranceMode());
        journal.setHisOrderNo(orderExtVo.getHisOrderNo());
        journal.setPayAppId(params.get("sub_appid"));
        journal.setOrderNo(orderExtVo.getOrderNo());
        journal.setHisOrderNo(orderExtVo.getHisOrderNo());
        journal.setPaymentDealId(params.get("med_trans_id"));
        journal.setPayStatus((short) 1);
        journal.setCheckStatus((short) 0);
        journal.setSettlementStatus((short) 0);
        journal.setPayCode(attach.getPayCode());
        journal.setPayMode((short) getPayModeByPayCode(attach.getPayCode()));
        journal.setDealAccount(params.get("sub_openid"));
        journal.setPayTime(DateUtils.parseDatetime(params.get("time_end"), DateUtils.YYYYMMDDHHMMSS_NOBAR));
        journal.setCreatedTime(DateUtils.getDatetime());

        logger.info("NotifyServiceImpl.createWechatMedicalJournal.journal -> {}", JsonUtils.toJsonString(journal));

        return journal;
    }

    /**
     * 微信普通支付通知逻辑
     *
     * @param params
     * @param data
     * @return
     */
    public String doOrdinaryWechatPayNotify(final Map<String, String> params, final String data) {
        WechatPayAttach wechatPayAttach = JsonUtils.parseObject(params.get("attach"), WechatPayAttach.class);

        // 1.产出消息(微信通知报文原样产出)
        try {
            if (wechatPayAttach != null && !StringUtils.isEmpty(wechatPayAttach.getTopic())) {
                produceMessage(wechatPayAttach.getTopic(), data);
            }
        } catch (Exception e) {
            logger.error("NotifyServiceImpl.doOrdinaryWechatPayNotify.produceMessage.Exception", e);
        }

        // 2.入流水(写入云医支付流水)
        try {
            insertPaymentJournal(createWechatJournal(params, wechatPayAttach));
        } catch (Exception e) {
            logger.error("NotifyServiceImpl.doOrdinaryWechatPayNotify.insertPaymentJournal.Exception", e);
        }

        return this.printWechatPayNotifySuccessMessage();
    }

    /**
     * 创建微信支付对应云医支付流水对象
     *
     * @param params
     * @param wechatPayAttach
     * @return
     */
    private PaymentJournal createWechatJournal(Map<String, String> params, WechatPayAttach wechatPayAttach) {
        PaymentJournal journal = new PaymentJournal();

        journal.setId(UUIDUtil.get32UUID());
        journal.setPaymentDealNo(NumberUtil.buildPaymentDealNo());

        OrderExtVo orderExtVo = this.getBaseOrderByOrderNo(params.get("out_trade_no"));
        journal.setOrgId(orderExtVo.getOrgId());
        journal.setOrgCode(orderExtVo.getOrgCode());
        journal.setOrgName(orderExtVo.getOrgName());
        journal.setPlatformId(orderExtVo.getPlatformId());
        journal.setPlatformName(orderExtVo.getPlatformName());
        journal.setPlatformOrgAppCode(orderExtVo.getPlatformOrgAppCode());
        journal.setPlatformOrgAppName(orderExtVo.getPlatformOrgAppName());
        journal.setBranchId(orderExtVo.getBranchId());
        journal.setBranchCode(orderExtVo.getBranchCode());
        journal.setBranchName(orderExtVo.getBranchName());
        journal.setOutOrderNo(orderExtVo.getOutOrderNo());
        journal.setOrderType(orderExtVo.getOrderType());
        journal.setAccountAmount(orderExtVo.getAccountAmount());
        journal.setMedicareAmount(orderExtVo.getMedicareAmount());
        journal.setInsuranceAmount(orderExtVo.getInsuranceAmount() == null ? 0 : orderExtVo.getInsuranceAmount());
        journal.setPayAmount(Integer.valueOf(params.get("total_fee")));
        journal.setTotalAmount(journal.getInsuranceAmount() + journal.getPayAmount());
        journal.setInsuranceMode(orderExtVo.getInsuranceMode());

        journal.setPayAppId(params.get("appid"));
        journal.setOrderNo(params.get("out_trade_no"));
        journal.setHisOrderNo(orderExtVo.getHisOrderNo());
        journal.setPaymentDealId(params.get("transaction_id"));
        journal.setPayStatus((short) 1);
        journal.setCheckStatus((short) 0);
        journal.setSettlementStatus((short) 0);
        journal.setPayCode(wechatPayAttach.getPayCode());
        journal.setPayMode((short) getPayModeByPayCode(wechatPayAttach.getPayCode()));
        journal.setDealAccount(params.get("openid"));
        journal.setPayTime(DateUtils.parseDatetime(params.get("time_end"), DateUtils.YYYYMMDDHHMMSS_NOBAR));
        journal.setCreatedTime(DateUtils.getDatetime());

        logger.info("NotifyServiceImpl.createWechatJournal.journal -> {}", JsonUtils.toJsonString(journal));

        return journal;
    }

    /**
     * 支付宝支付通知
     *
     * @param data
     * @return
     */
    public String alipayNotify(String data) {
        logger.info("NotifyServiceImpl.alipayNotify.data -> {}", data);

        try {
            Map<String, Object> params = JsonUtils.parseObject(data, Map.class);
            logger.info("NotifyServiceImpl.alipayNotify.params -> {}", params);
            if (CollectionUtils.isEmpty(params)) return FAILURE;

            String payCode = (String) params.get("pay_code");
            if (!StringUtils.isEmpty(payCode) &&
                    (PayCode.valueOf(payCode).equals(PayCode.ALIPAYBARPAY) ||
                            PayCode.valueOf(payCode).equals(PayCode.ALIPAYWAVEPAY) ||
                            PayCode.valueOf(payCode).equals(PayCode.ALIPAYMEDICAREBARPAY) ||
                            PayCode.valueOf(payCode).equals(PayCode.ALIPAYMEDICAREWAVEPAY))) {
                logger.info("PayCode [{}] no need to verify signature", payCode);
            } else {
                String sign = (String) params.remove("sign");
                String signType = (String) params.remove("sign_type");
                String charset = (String) params.get("charset");
                String appId = (String) params.get("app_id");
                PaymentSetting paymentSetting = getPaymentSetting(appId);
                boolean isValid = AlipaySignature.rsaCheck(
                        SignUtil.buildAlipaySignContent(params),
                        sign, paymentSetting.getPayPublicKey(), charset, signType
                );
                logger.info("NotifyServiceImpl.alipayNotify.isValid -> {}", isValid);
                if (!isValid) return FAILURE;
            }

            String tradeStatus = (String) params.get("trade_status");
            if (!Alipay.TradeStatus.TRADE_SUCCESS.equals(Alipay.TradeStatus.valueOf(tradeStatus))) {
                return SUCCESS;
            }
            if (params.containsKey("refund_fee")) {
                return SUCCESS;
            }

            UnifiedPayNotifyMessage unifiedMessage = buildAlipayUnifiedMessage(params);
            notifyExecutor.execute(new PayNotifyWorker(unifiedMessage, data));

            return SUCCESS;
        } catch (Exception e) {
            logger.error("NotifyServiceImpl.alipayNotify.exception", e);
            return FAILURE;
        }
    }

    /**
     * 支付宝支付通知逻辑
     *
     * @param params
     * @param data
     * @return
     */
    private String doAlipayNotify(final Map<String, Object> params, final String data) {
        AlipayAttach alipayAttach = JsonUtils.parseObject(params, AlipayAttach.class);

        OrderExtVo orderExtVo = this.getBaseOrderByOrderNo((String) params.get("out_trade_no"));
        UnifiedPayNotifyMessage unifiedMessage = buildAlipayUnifiedMessage(params);

        // 1.产出消息(支付宝通知报文原样产出)
        try {
            if (alipayAttach != null && !StringUtils.isEmpty(alipayAttach.getTopic())) {
                switch (KafkaTopic.valueOf(alipayAttach.getTopic())) {
                    case REGISTER_ALIPAY_PAY:
                        produceMessage(alipayAttach.getTopic(), JsonUtils.toJsonString(unifiedMessage));
                        break;
                    default:
                        produceMessage(alipayAttach.getTopic(), data);
                        break;
                }
            }
        } catch (Exception e) {
            logger.error("NotifyServiceImpl.doAlipayNotify.produceMessage.Exception", e);
        }

        // 2.入流水(写入云医支付流水)
        try {
            insertPaymentJournal(createAlipayJournal(params, orderExtVo, unifiedMessage));
        } catch (Exception e) {
            logger.error("NotifyServiceImpl.doAlipayNotify.insertPaymentJournal.Exception", e);
        }

        return SUCCESS;
    }

    /**
     * 创建支付宝支付对应云医支付流水对象
     *
     * @param params
     * @return
     */
    private PaymentJournal createAlipayJournal(Map<String, Object> params, OrderExtVo orderExtVo, UnifiedPayNotifyMessage unifiedMessage) {
        PaymentJournal journal = new PaymentJournal();

        journal.setId(UUIDUtil.get32UUID());
        journal.setPaymentDealNo(NumberUtil.buildPaymentDealNo());

        journal.setOrgId(orderExtVo.getOrgId());
        journal.setOrgCode(orderExtVo.getOrgCode());
        journal.setOrgName(orderExtVo.getOrgName());
        journal.setPlatformId(orderExtVo.getPlatformId());
        journal.setPlatformName(orderExtVo.getPlatformName());
        journal.setPlatformOrgAppCode(orderExtVo.getPlatformOrgAppCode());
        journal.setPlatformOrgAppName(orderExtVo.getPlatformOrgAppName());
        journal.setBranchId(orderExtVo.getBranchId());
        journal.setBranchCode(orderExtVo.getBranchCode());
        journal.setBranchName(orderExtVo.getBranchName());
        journal.setOutOrderNo(orderExtVo.getOutOrderNo());
        journal.setOrderType(orderExtVo.getOrderType());
        journal.setHisOrderNo(orderExtVo.getHisOrderNo());
        journal.setPayAmount(new BigDecimal(unifiedMessage.getPayAmount()).intValue());

        String insuranceAmount = unifiedMessage.getInsuranceAmount();

        if (StringUtils.isEmpty(insuranceAmount)) {
            journal.setAccountAmount(orderExtVo.getAccountAmount());
            journal.setMedicareAmount(orderExtVo.getMedicareAmount());
            journal.setInsuranceAmount(orderExtVo.getInsuranceAmount() == null ? 0 : orderExtVo.getInsuranceAmount());
        } else {
            journal.setAccountAmount(new BigDecimal(insuranceAmount).intValue());
            journal.setMedicareAmount(orderExtVo.getMedicareAmount());
            journal.setInsuranceAmount(new BigDecimal(insuranceAmount).intValue());
        }

        journal.setTotalAmount(new BigDecimal(unifiedMessage.getTotalAmount()).intValue());

        journal.setInsuranceMode(orderExtVo.getInsuranceMode());

        String payCode = (String) params.get("pay_code");

        journal.setPayAppId((String) params.get("app_id"));
        journal.setOrderNo((String) params.get("out_trade_no"));
        journal.setPaymentDealId((String) params.get("trade_no"));
        journal.setPayStatus((short) 1);
        journal.setCheckStatus((short) 0);
        journal.setSettlementStatus((short) 0);
        journal.setPayCode(payCode);
        journal.setPayMode((short) getPayModeByPayCode(payCode));

        if (!StringUtils.isEmpty(params.get("buyer_id"))) {
            journal.setDealAccount((String) params.get("buyer_id"));
        } else if (!StringUtils.isEmpty(params.get("buyer_user_id"))) {
            journal.setDealAccount((String) params.get("buyer_user_id"));
        }
        journal.setPayTime((String) params.get("gmt_payment"));
        journal.setCreatedTime(DateUtils.getDatetime());

        logger.info("NotifyServiceImpl.createAlipayJournal.journal -> {}", JsonUtils.toJsonString(journal));

        return journal;
    }

    /**
     * 平安付支付通知
     *
     * @param data
     * @return
     */
    public String pacityNotify(String data) {
        logger.info("NotifyServiceImpl.pacityNotify.data -> {}", data);

        try {
            boolean isSignValid = SignUtil.verifyPacitySign(JSON.parseObject(data, Map.class), getConfig(Pacity.PACITY_PUBLIC_KEY));
            logger.info("NotifyServiceImpl.pacityNotify.isSignValid -> {}", isSignValid);
            if (!isSignValid) return printPacityPayNotifyFailMessage("SIGN_ERROR");

            PacityPayNotifyMessage pacityMessage = JSON.parseObject(data, PacityPayNotifyMessage.class);
            logger.info("NotifyServiceImpl.pacityNotify.pacityMessage -> {}", JSON.toJSONString(pacityMessage));

            String payResult = pacityMessage.getPayResult();
            switch (cn.yunyichina.provider.pay.constant.Pacity.PayResult.valueOf(payResult)) {
                case PAY_SUCCESS:
                    UnifiedPayNotifyMessage unifiedMessage = buildPacityUnifiedMessage(pacityMessage);
                    logger.info("NotifyServiceImpl.pacityNotify.unifiedMessage -> {}", JSON.toJSONString(unifiedMessage));

                    notifyExecutor.execute(new PayNotifyWorker(unifiedMessage, data));

                    break;
                case CLOSED:
                    logger.info("NotifyServiceImpl.pacityNotify.PayResult.CLOSED -> {}", pacityMessage.getPlatMchTradeNo());
                    break;
                default:
                    logger.info("NotifyServiceImpl.pacityNotify.PayResult.DEFAULT -> {}", pacityMessage.getPlatMchTradeNo());
                    break;
            }

            return printPacityPayNotifySuccessMessage();
        } catch (Exception e) {
            logger.error("NotifyServiceImpl.pacityNotify.Exception", e);
            return printPacityPayNotifyFailMessage("EXCEPTION");
        }
    }

    /**
     * 产出支付通知消息到消息队列(业务方监听消息完成支付闭环)
     *
     * @param topic
     * @param message
     */
    private void produceMessage(String topic, String message) {
        if (StringUtils.isEmpty(topic)) return;
        logger.info("NotifyServiceImpl.produceMessage.topic -> {}", topic);
        logger.info("NotifyServiceImpl.produceMessage.message -> {}", message);

        String kafkaTopic = KafkaTopic.valueOf(topic).getTopic();
        logger.info("NotifyServiceImpl.produceMessage.kafkaTopic -> {}", kafkaTopic);

        ListenableFuture<SendResult<Integer, String>> listenableFuture = kafkaTemplate.send(kafkaTopic, message);

        listenableFuture.addCallback(new ListenableFutureCallback<SendResult<Integer, String>>() {
            public void onSuccess(SendResult<Integer, String> sendResult) {
                logger.info("NotifyServiceImpl.produceMessage.onSuccess.getProducerRecord -> {}", sendResult.getProducerRecord().toString());
                logger.info("NotifyServiceImpl.produceMessage.onSuccess.getRecordMetadata -> {}", sendResult.getRecordMetadata().toString());
            }

            public void onFailure(Throwable throwable) {
                logger.error("NotifyServiceImpl.produceMessage.onFailure", throwable);
            }
        });
    }

    /**
     * 新增云医支付流水
     *
     * @param paymentJournal
     */
    private void insertPaymentJournal(PaymentJournal paymentJournal) {
        paymentJournalDao.insert(paymentJournal);
    }

    /**
     * 查询流水是否入库成功，以此判断支付是否成功处理通知
     *
     * @param orderNo
     * @param paymentDealId
     * @return
     */
    private boolean isPayNotified(String orderNo, String paymentDealId) {
        PaymentJournal paymentJournal = paymentJournalService.getPaymentJournalByOrderNoAndPaymentDealIdAndPayStatus(orderNo, paymentDealId, (short) 1);
        boolean result = paymentJournal != null;
        logger.info("NotifyServiceImpl.isPayNotified.result -> {}", result);
        return result;
    }

    /**
     * 构建支付宝统一通知对象
     *
     * @param params
     * @return
     */
    public UnifiedPayNotifyMessage buildAlipayUnifiedMessage(Map<String, Object> params) {
        UnifiedPayNotifyMessage message = new UnifiedPayNotifyMessage();
        message.setTradeStatus((String) params.get("trade_status"));
        message.setAppId((String) params.get("app_id"));
        message.setOpenId(StringUtils.isEmpty(params.get("buyer_id")) ? (String) params.get("buyer_user_id") : (String) params.get("buyer_id"));
        message.setOrderNo((String) params.get("out_trade_no"));
        message.setTradeNo((String) params.get("trade_no"));
        message.setPayTime(params.get("gmt_payment") == null ? params.get("send_pay_date") == null ? DateUtils.getDateTimeStr() : (String) params.get("send_pay_date") : (String) params.get("gmt_payment"));
        message.setMedicalContent((String) params.get("industry_sepc_detail"));

        message.setPayAmount(params.get("buyer_pay_amount") == null ? "0" : params.get("buyer_pay_amount").toString());

        String insuranceAmount = "0";

        Object fundBillListObject = params.get("fund_bill_list");

        List<Map<String, String>> fundBillList = null;
        if (fundBillListObject instanceof String) {
            fundBillList = JsonUtils.parseObject(params.get("fund_bill_list"), List.class);
        } else if (fundBillListObject instanceof List) {
            fundBillList = (List<Map<String, String>>) params.get("fund_bill_list");
        }

        if (!CollectionUtils.isEmpty(fundBillList)) {
            BigDecimal payAmount = new BigDecimal(0);

            for (Map<String, String> map : fundBillList) {
                Alipay.FundBill fundBill = JsonUtils.parseObject(map, Alipay.FundBill.class);

                assert fundBill != null;
                switch (Alipay.FundChannel.valueOf(fundBill.getFundChannel())) {
                    case OUTSIDECARD:
                        insuranceAmount = fundBill.getAmount();
                        break;
                    default:
                        payAmount = payAmount.add(new BigDecimal(fundBill.getAmount()));
                        break;
                }
            }

            message.setPayAmount(payAmount.multiply(new BigDecimal(100)).toString());
        }

        String receiptAmount = (String) params.get("receipt_amount");
        if (!StringUtils.isEmpty(receiptAmount)) {
            message.setPayAmount(new BigDecimal((String) params.get("receipt_amount")).multiply(new BigDecimal(100)).toString());
        }

        message.setInsuranceAmount(new BigDecimal(insuranceAmount).multiply(new BigDecimal(100)).toString());

        message.setTotalAmount(new BigDecimal(message.getPayAmount()).add(new BigDecimal(message.getInsuranceAmount())).toString());

        MchData passbackParams = JsonUtils.parseObject((String) params.get("passback_params"), MchData.class);

        String payCode = params.get("pay_code") == null ? passbackParams != null ? passbackParams.getPayCode() : "" : params.get("pay_code").toString();
        String payMode = params.get("pay_mode") == null ? passbackParams != null ? passbackParams.getPayMode() : "" : params.get("pay_mode").toString();
        String topic = params.get("topic") == null ? passbackParams != null ? passbackParams.getTopic() : "" : params.get("topic").toString();
        MchData mchData = new MchData();
        mchData.setPayCode(payCode);
        mchData.setPayMode(payMode);
        mchData.setTopic(topic);
        switch (PayCode.valueOf(payCode)) {
            case ALIPAYMEDICARE:
                mchData.setIsInsurance(Medicare.INSURANCE_MODE_YEP);
                break;
            case ALIPAYMEDICAREBARPAY:
                mchData.setIsInsurance(Medicare.INSURANCE_MODE_YEP);
                break;
            case ALIPAYMEDICAREWAVEPAY:
                mchData.setIsInsurance(Medicare.INSURANCE_MODE_YEP);
                break;
            case ALIPAYMEDINFOUPLOAD:
                mchData.setIsInsurance(Medicare.INSURANCE_MODE_YEP);
                break;
            default:
                mchData.setIsInsurance(Medicare.INSURANCE_MODE_NOPE);
                break;
        }
        message.setMchData(mchData);

        return message;
    }

    /**
     * 构建平安付统一通知对象
     *
     * @param pacityMessage
     * @return
     */
    private UnifiedPayNotifyMessage buildPacityUnifiedMessage(PacityPayNotifyMessage pacityMessage) {
        UnifiedPayNotifyMessage unifiedMessage = new UnifiedPayNotifyMessage();
        unifiedMessage.setTradeStatus(pacityMessage.getPayResult());
        if (!StringUtils.isEmpty(pacityMessage.getSelfpayAmt())) {
            unifiedMessage.setPayAmount(new BigDecimal(pacityMessage.getSelfpayAmt()).multiply(new BigDecimal(100)).toString());
        }
        if (!StringUtils.isEmpty(pacityMessage.getSsPayAmt())) {
            unifiedMessage.setInsuranceAmount(new BigDecimal(pacityMessage.getSsPayAmt()).multiply(new BigDecimal(100)).toString());
        }
        if (!StringUtils.isEmpty(pacityMessage.getTotalAmt())) {
            unifiedMessage.setTotalAmount(new BigDecimal(pacityMessage.getTotalAmt()).multiply(new BigDecimal(100)).toString());
        }
        unifiedMessage.setOrderNo(pacityMessage.getPlatMchTradeNo());
        unifiedMessage.setTradeNo(pacityMessage.getYztTradeNo());
        String tradeFinishTime = pacityMessage.getTradeFinishTime();
        if (StringUtils.isEmpty(tradeFinishTime)) {
            unifiedMessage.setPayTime(DateUtils.getDateTimeStr());
        } else {
            unifiedMessage.setPayTime(DateUtils.strToDateTimeYMDHMSNotFilter(tradeFinishTime));
        }
        unifiedMessage.setMedicalContent(pacityMessage.getDataFromPayComp());

        MchData mchData = JsonUtils.parseObject(pacityMessage.getMchEchoData(), MchData.class);
        unifiedMessage.setMchData(mchData);
        assert mchData != null;
        unifiedMessage.setAppId(mchData.getAppId());

        return unifiedMessage;
    }

}
